using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using DanielSig.Geometry;
using DanielSig;

namespace DanielSig.UnityExtensions
{
	public enum TransformationOptions
	{
		OnlyVertices = 0,
		Normals = 1,
		Tangents = 2,
		All = 3,
	}
	
	public class VertexList
	{
		public readonly Vector3[] Positions;
		public readonly Vector2[] UVs;
		public readonly int[] Indices;
		
		internal VertexList(Vector3[] positions, Vector2[] uvs, int[] indices)
		{
			Positions = positions;
			UVs = uvs;
			Indices = indices;
		}
	}
	public struct Vertex
	{
		
		public static Vertex NULL = new Vertex(Vector3.zero, Vector3.zero, Vector4.zero, Vector2.zero, Vector2.zero, Vector2.zero, -1);
		
		public readonly Vector3 Position;
		public readonly Vector3 Normal;
		public readonly Vector4 Tangent;
		public readonly Vector2 UV;
		public readonly Vector2 UV1;
		public readonly Vector2 UV2;
		public readonly int Index;
		
		internal Vertex(Vector3 position, Vector3 normal, Vector4 tangent, Vector2 uv, Vector2 uv1, Vector2 uv2, int index)
		{
			Position = position;
			Normal = normal;
			Tangent = tangent;
			UV = uv;
			UV1 = uv1;
			UV2 = uv2;
			Index = index;
		}
		internal Vertex(Vector3[] positions, Vector3[] normals, Vector4[] tangents, Vector2[] uvs, Vector2[] uvs1, Vector2[] uvs2, int index, int overrideIndex = -1)
		{
			Position = (positions != null && positions.Length > index) ? positions[index] : Vector3.zero;
			Normal = (normals != null && normals.Length > index) ? normals[index] : Vector3.zero;
			Tangent = (tangents != null && tangents.Length > index) ? tangents[index] : Vector4.zero;
			UV = (uvs != null && uvs.Length > index) ? uvs[index] : Vector2.zero;
			UV1 = (uvs1 != null && uvs1.Length > index) ? uvs1[index] : Vector2.zero;
			UV2 = (uvs2 != null && uvs2.Length > index) ? uvs2[index] : Vector2.zero;
			Index = overrideIndex > -1 ? overrideIndex : index;
		}
		public static bool operator ==(Vertex left, Vertex right)
		{
			return left.Index == right.Index;
		}
		public static bool operator !=(Vertex left, Vertex right)
		{
			return left.Index != right.Index;
		}
		public override bool Equals(object other)
		{
			if(other is Vertex) return this.Index == ((Vertex)other).Index;
			else return false;
		}
		public override int GetHashCode()
		{
			return Index;
		}
	}
	public static class XMesh
	{
		
		#region CLONE
		
		public static Mesh Clone(this Mesh mesh)
		{
			if(mesh == null)
			{
				return null;
			}
			Mesh output = new Mesh();
			mesh.CopyTo(output);
			output.name = mesh.name + "(clone)";
			return output;
		}
		
		#endregion
		
		#region SET
		
		public static void CopyTo(this Mesh mesh, Mesh destination)
		{
			if(mesh == null || destination == null) return;
			
			destination.Clear(false);
			destination.vertices = (Vector3[])mesh.vertices.Clone();
			destination.triangles = (int[])mesh.triangles.Clone();
			destination.uv = (Vector2[])mesh.uv.Clone();
			destination.uv1 = (Vector2[])mesh.uv1.Clone();
			destination.uv2 = (Vector2[])mesh.uv2.Clone();
			destination.normals = (Vector3[])mesh.normals.Clone();
			destination.tangents = (Vector4[])mesh.tangents.Clone();
			destination.RecalculateBounds();
			destination.name = mesh.name + "(copy)";
		}
		
		#endregion
		
		#region ANALAZIS
		
		public static void GetRandomSample(this Mesh mesh, ref Vector3[] sample)
		{
			Vector3[] vertices = mesh.vertices;
			float vertexCount = mesh.vertexCount;
			float interval = vertexCount * 0.618033989F;//golden ratio
			float verticeIndex = 0;
			int numSamples = sample.Length;
			for(int i = 0; i < numSamples; i++)
			{
				verticeIndex = (verticeIndex + interval) % vertexCount;
				sample[i] = vertices[(int)verticeIndex];
			}
		}
		public static Vector3[] GetRandomSample(this Mesh mesh, int amount)
		{
			Vector3[] sample = new Vector3[amount];
			Vector3[] vertices = mesh.vertices;
			float vertexCount = mesh.vertexCount;
			float interval = vertexCount * 0.618033989F;//golden ratio
			float verticeIndex = 0;
			int numSamples = sample.Length;
			for(int i = 0; i < numSamples; i++)
			{
				verticeIndex = (verticeIndex + interval) % vertexCount;
				sample[i] = vertices[(int)verticeIndex];
			}
			return sample;
		}
		public static void GetSample(this Mesh mesh, ref Vector3[] sample)
		{
			Vector3[] vertices = mesh.vertices;
			float interval = (mesh.vertexCount - 1) / ((float)sample.Length);
			float verticeIndex = 0;
			int numSamples = sample.Length;
			for(int i = 0; i < numSamples; i++)
			{
				verticeIndex += interval;
				sample[i] = vertices[(int)verticeIndex];
			}
		}
		public static Vector3[] GetSample(this Mesh mesh, int amount)
		{
			Vector3[] sample = new Vector3[amount];
			Vector3[] vertices = mesh.vertices;
			float interval = (mesh.vertexCount - 1) / ((float)amount);
			float verticeIndex = 0;
			int numSamples = sample.Length;
			for(int i = 0; i < numSamples; i++)
			{
				verticeIndex += interval;
				sample[i] = vertices[(int)verticeIndex];
			}
			return sample;
		}
		
		#endregion
		
		#region VOLUME, SURFACE AND BOUNDS
		///<summary>
		///Works for convex and near convex meshes.
		///</summary>
		public static float GetVolume(this Mesh mesh)
		{
			if(mesh == null)
			{
				return 0;
			}
			float volume = 0;
			int[] triangles = mesh.triangles;
			Vector3[] vertices = mesh.vertices;
			int numTriangles = triangles.Length;
			for(int i = 0; i < numTriangles;)
			{
				volume += SignedVolumeOfTriangle(vertices[triangles[i++]], vertices[triangles[i++]], vertices[triangles[i++]]);
			}
			return volume;
		}
		public static float GetSurface(this Mesh mesh)
		{
			Vector3 totalSurface = Vector3.zero;
			int[] triangles = mesh.triangles;
			Vector3[] vertices = mesh.vertices;
			int numTriangles = triangles.Length;
			for (int i = 0; i < numTriangles; i += 3) {
				totalSurface += Geom.Abs(Vector3.Cross(vertices[triangles[i + 1]] - vertices[triangles[i]], vertices[triangles[i + 2]] - vertices[triangles[i]]));
			}
			return (totalSurface).magnitude * 0.5F;
		}
		public static float GetSurface(Vector3[] corners)
		{
			Vector3 totalSurface = Vector3.zero;
			int numTriangles = corners.Length - 2;
			for (int i = 2; i < numTriangles; i++) {
				totalSurface += Vector3.Cross(corners[i - 1] - corners[0], corners[i] - corners[0]);
			}
			return (totalSurface).magnitude * 0.5F;
		}
		public static Vector3 GetFurthestVertexFrom(this Mesh mesh, Vector3 vert)
		{
			int numVertices = mesh.vertexCount;
			float max = float.NegativeInfinity;
			Vector3 maxVertex = vert;
			Vector3[] vertices = mesh.vertices;
			for (int i = 0; i < numVertices; i++)
			{
				float dist = (vertices[i] - vert).sqrMagnitude;
				if(dist > max)
				{
					max = dist;
					maxVertex = vertices[i];
				}
			}
			return maxVertex;
		}
		public static Vector3 GetNearestVertexTo(this Mesh mesh, Vector3 vert)
		{
			int numVertices = mesh.vertexCount;
			float min = float.PositiveInfinity;
			Vector3 minVertex = vert;
			Vector3[] vertices = mesh.vertices;
			for (int i = 0; i < numVertices; i++)
			{
				float dist = (vertices[i] - vert).sqrMagnitude;
				if(dist < min)
				{
					min = dist;
					minVertex = vertices[i];
				}
			}
			return minVertex;
		}
		
		public static float GetMinimumSpanningRadius(this Mesh mesh)
		{
			int numVertices = mesh.vertexCount;
			float max = float.NegativeInfinity;
			Vector3[] vertices = mesh.vertices;
			for (int i = 0; i < numVertices; i++)
			{
				float dist = vertices[i].sqrMagnitude;
				if(dist > max) max = dist;
			}
			return Mathf.Sqrt(max);
		}
		public static float GetMinimumSpanningRadius(this Mesh mesh, Vector3 vert)
		{
			int numVertices = mesh.vertexCount;
			float max = float.NegativeInfinity;
			Vector3[] vertices = mesh.vertices;
			for (int i = 0; i < numVertices; i++)
			{
				float dist = (vertices[i] - vert).sqrMagnitude;
				if(dist > max) max = dist;
			}
			return Mathf.Sqrt(max);
		}
		public static Vector3 GetCenter(this Mesh mesh)
		{
			return mesh != null ? mesh.bounds.center : Vector3.zero;
		}
		#endregion
		
		#region LINQ
		
		public static TAccumulate Aggregate<TAccumulate>(this Mesh mesh, TAccumulate seed, Func<TAccumulate, Vertex, TAccumulate> func)
		{
			return mesh.Aggregate<TAccumulate, TAccumulate>(seed, func, x => x);
		}
		public static TResult Aggregate<TAccumulate, TResult>
		(
			this Mesh mesh,
			TAccumulate seed,
			Func<TAccumulate, Vertex, TAccumulate> func,
			Func<TAccumulate, TResult> resultSelector
		)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			
			TAccumulate value = seed;
			
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				value = func(value, v);
			}
			return resultSelector(value);
		}
		public static bool Any(this Mesh mesh, Func<Vertex, bool> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				if(predicate(v)) return true;
			}
			return false;
		}
		public static bool All(this Mesh mesh, Func<Vertex, bool> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				if(!predicate(v)) return false;
			}
			return true;
		}
		public static LinkedList<Vertex> FindVertices(this Mesh mesh, Func<Vertex, bool> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			LinkedList<Vertex> list = new LinkedList<Vertex>();
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				if(predicate(v)) list.AddLast(v);
			}
			return list;
		}
		public static Vertex FindVertex(this Mesh mesh, Func<Vertex, bool> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				if(predicate(v)) return v;
			}
			return Vertex.NULL;
		}
		public static Vertex GetBestMatchingVertex(this Mesh mesh, Func<Vertex, float> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			
			Vertex bestVertex = Vertex.NULL;
			float bestValue = float.NegativeInfinity;
			
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				float val = predicate(v);
				if(val > bestValue)
				{
					bestValue = val;
					bestVertex = v;
				}
			}
			return bestVertex;
		}
		public static Vertex GetBestMatchingVertexInversed(this Mesh mesh, Func<Vertex, float> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			
			Vertex bestVertex = Vertex.NULL;
			float bestValue = float.PositiveInfinity;
			
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				float val = predicate(v);
				if(val < bestValue)
				{
					bestValue = val;
					bestVertex = v;
				}
			}
			return bestVertex;
		}
		public static void ForEach(this Mesh mesh, Action<Vertex> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				predicate(v);
			}
		}
		public static void ForEach(this Mesh mesh, Func<Vertex, Vertex> predicate)
		{
			int numVertices = mesh.vertexCount;
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			
			bool useNormals = normals != null && normals.Length > 0;
			bool useTangents = tangents != null && tangents.Length > 0;
			bool useUV = uvs != null && uvs.Length > 0;
			bool useUV1 = uvs1 != null && uvs1.Length > 0;
			bool useUV2 = uvs2 != null && uvs2.Length > 0;
			
			for (int i = 0; i < numVertices; i++)
			{
				Vertex v = new Vertex(vertices, normals, tangents, uvs, uvs1, uvs2, i);
				v = predicate(v);
				vertices[i] = v.Position;
				if(useNormals) normals[i] = v.Normal;
				if(useTangents) tangents[i] = v.Tangent;
				if(useUV) uvs[i] = v.UV;
				if(useUV1) uvs1[i] = v.UV1;
				if(useUV2) uvs2[i] = v.UV2;
			}
			mesh.vertices = vertices;
			mesh.normals = normals;
			mesh.tangents = tangents;
			mesh.uv = uvs;
			mesh.uv1 = uvs1;
			mesh.uv2 = uvs2;
		}
		
		#endregion
		
		#region SCALE
		public static Mesh GetScaled(this Mesh mesh, float x, float y, float z, TransformationOptions options = TransformationOptions.All)
		{
			return mesh.GetScaled(new Vector3(x, y, z), options);
		}
		public static Mesh GetScaled(this Mesh mesh, float scale)
		{
			return mesh.GetScaled(new Vector3(scale, scale, scale), TransformationOptions.OnlyVertices);
		}
		public static Mesh GetScaled(this Mesh mesh, Vector3 scalar, TransformationOptions options = TransformationOptions.All)
		{
			return mesh.GetTransformed(Matrix4x4.Scale(scalar), options);
		}
		
		
		public static void Scale(this Mesh mesh, float x, float y, float z, TransformationOptions options = TransformationOptions.All)
		{
			mesh.Scale(new Vector3(x, y, z), options);
		}
		public static void Scale(this Mesh mesh, float scale)
		{
			mesh.Scale(new Vector3(scale, scale, scale), TransformationOptions.OnlyVertices);
		}
		public static void Scale(this Mesh mesh, Vector3 scalar, TransformationOptions options = TransformationOptions.All)
		{
			mesh.Transform(Matrix4x4.Scale(scalar), options);
		}
		#endregion
		
		#region ROTATION
		public static Mesh GetRotated(this Mesh mesh, Quaternion rotation)
		{
			return mesh.GetTransformed(Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one));
		}
		
		public static void Rotate(this Mesh mesh, Quaternion rotation)
		{
			mesh.Transform(Matrix4x4.TRS(Vector3.zero, rotation, Vector3.one));
		}
		#endregion
		
		#region TRANSFORM
		public static Mesh GetTransformed(this Mesh mesh, Matrix4x4 matrix, TransformationOptions options = TransformationOptions.All)
		{
			if(mesh == null)
			{
				return null;
			}
			Mesh output = new Mesh();
			output.vertices = TransformVertices(mesh, matrix);
			output.triangles = mesh.triangles;
			output.uv = mesh.uv;
			output.uv1 = mesh.uv1;
			output.uv2 = mesh.uv2;
			output.name = mesh.name + "(transformed)";
			
			if((options & TransformationOptions.Normals) > 0) output.normals = TransformNormals(mesh, matrix);
			else output.normals = mesh.normals;
			
			if((options & TransformationOptions.Tangents) > 0) output.tangents = TransformTangents(mesh, matrix);
			else output.tangents = mesh.tangents;
			output.RecalculateBounds();
			return output;
		}
		public static void Transform(this Mesh mesh, Vector3 translation, Quaternion rotation, Vector3 scale, TransformationOptions options = TransformationOptions.All)
		{
			mesh.Transform(Matrix4x4.TRS(translation, rotation, scale), options);
		}
		public static void Transform(this Mesh mesh, Matrix4x4 matrix, TransformationOptions options = TransformationOptions.All)
		{
			if(mesh == null)
			{
				return;
			}
			mesh.vertices = TransformVertices(mesh, matrix);
			if((options & TransformationOptions.Normals) > 0) mesh.normals = TransformNormals(mesh, matrix);
			if((options & TransformationOptions.Tangents) > 0) mesh.tangents = TransformTangents(mesh, matrix);
			mesh.RecalculateBounds();
		}
		private static Vector3[] TransformVertices(Mesh mesh, Matrix4x4 matrix)
		{
			Vector3[] vertices = mesh.vertices;
			int numVertices = vertices.Length;
			
			for(int i = 0; i < numVertices; i++)
			{
				vertices[i] = matrix.MultiplyPoint3x4(vertices[i]);
			}
			return vertices;
		}
		private static Vector3[] TransformNormals(Mesh mesh, Matrix4x4 matrix)
		{
			Vector3[] normals = mesh.normals;
			if(normals == null || normals.Length == 0)
			{
				//Debug.LogWarning("Could not transform mesh normals because the given mesh doesn't have any: " + mesh.name);
				return null;
			}
			
			int numNormals = normals.Length;
			
			matrix = matrix.inverse.transpose;
			
			for(int i = 0; i < numNormals; i++)
			{
				normals[i] = matrix.MultiplyVector(normals[i]).normalized;
			}
			return normals;
		}
		private static Vector4[] TransformTangents(Mesh mesh, Matrix4x4 matrix)
		{
			Vector4[] tangents = mesh.tangents;
			if(tangents == null || tangents.Length == 0)
			{
				//Debug.LogWarning("Could not transform mesh tangents because the given mesh doesn't have any: " + mesh.name);
				return null;
			}
			
			int numTangents = tangents.Length;
			
			for(int i = 0; i < numTangents; i++)
			{
				tangents[i] = matrix.MultiplyVector(tangents[i].XYZ()).normalized.Join(tangents[i].w);
			}
			return tangents;
		}
		#endregion
		
		#region REMOVE DUPLICATES
		
		public enum DuplicatesDefinition
		{
			SamePosition = 1,
			SameUV = 2,
			SameNormal = 4,
			SameTangent = 8,
			SameUV1 = 16,
			SameUV2 = 32,
			All = 63,
			Default = 3,
		}
		
		public static Mesh NoDuplicateVertices(this Mesh mesh)
		{
			Mesh clone = mesh.Clone();
			clone.RemoveDuplicateVertices();
			return clone;
		}
		public static void RemoveDuplicateVertices(this Mesh mesh, DuplicatesDefinition definition = DuplicatesDefinition.Default)
		{
			int[] triangles = mesh.triangles;
			Vector3[] vertices = mesh.vertices;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1;
			Vector2[] uvs2 = mesh.uv2;
			Vector4[] tangents = mesh.tangents;
			Vector3[] normals = mesh.normals;
			
			int vertexCount = mesh.vertexCount;
			int newVertexCount = vertexCount;
			int triangleCount = triangles.Length;
			int currentShift = 0;
			bool useUV = mesh.uv != null && mesh.uv.Length > 0;
			bool useUV1 = mesh.uv1 != null && mesh.uv1.Length > 0;
			bool useUV2 = mesh.uv2 != null && mesh.uv2.Length > 0;
			bool useTangents = mesh.tangents != null && mesh.tangents.Length > 0;
			bool useNormals = mesh.normals != null && mesh.normals.Length > 0;
			
			int[] shift = new int[vertexCount];
			bool[] flags = new bool[vertexCount];
			int[] newTriangles = mesh.triangles as int[];
			Vector3[] newVertices;
			Vector2[] newUV = null;
			Vector2[] newUV1 = null;
			Vector2[] newUV2 = null;
			Vector4[] newTangents = null;
			Vector3[] newNormals = null;
			
			bool IgnorePosition = (definition & DuplicatesDefinition.SamePosition) == 0;
			bool IgnoreUV = (definition & DuplicatesDefinition.SameUV) == 0;
			bool IgnoreUV1 = (definition & DuplicatesDefinition.SameUV1) == 0;
			bool IgnoreUV2 = (definition & DuplicatesDefinition.SameUV2) == 0;
			bool IgnoreTangent = (definition & DuplicatesDefinition.SameTangent) == 0;
			bool IgnoreNormal = (definition & DuplicatesDefinition.SameNormal) == 0;
			
			for(int i = 0; i < vertexCount; i++)
			{
				if(flags[i])
				{
					currentShift++;
				}
				else
				{
					for(int j = i+1; j < vertexCount; j++)
					{
						if((IgnorePosition	|| vertices[i] == vertices[j])
						&& (IgnoreUV		|| uvs[i] == uvs[j])
						&& (IgnoreNormal	|| normals[i] == normals[j])
						&& (IgnoreTangent	|| tangents[i] == tangents[j])
						&& (IgnoreUV1		|| uvs1[i] == uvs1[j])
						&& (IgnoreUV2		|| uvs2[i] == uvs2[j]))
						{
							flags[j] = true;
							shift[j] = j - (i - currentShift);
						}
					}
					shift[i] = currentShift;
				}
			}
			
			newVertexCount -= currentShift;
			newVertices = new Vector3[newVertexCount];
			if(useUV)
			{
				newUV = new Vector2[newVertexCount];
			}
			if(useUV1)
			{
				newUV1 = new Vector2[newVertexCount];
			}
			if(useUV2)
			{
				newUV2 = new Vector2[newVertexCount];
			}
			if(useTangents)
			{
				newTangents = new Vector4[newVertexCount];
			}
			if(useNormals)
			{
				newNormals = new Vector3[newVertexCount];
			}
		
			
			for(int i = 0; i < vertexCount; i++)
			{
				//Debug.Log(i + ", " + shift[i] + ", " + vertexCount + ", " + newVertexCount + ", " + flags.Length);
				if(!flags[i])
				{
					newVertices[i - shift[i]] = vertices[i];
					if(useUV)
					{
						newUV[i - shift[i]] = uvs[i];
					}
					if(useUV1)
					{
						newUV1[i - shift[i]] = uvs1[i];
					}
					if(useUV2)
					{
						newUV2[i - shift[i]] = uvs2[i];
					}
					if(useTangents)
					{
						newTangents[i - shift[i]] = tangents[i];
					}
					if(useNormals)
					{
						newNormals[i - shift[i]] = normals[i];
					}
				}
			}
			for(int i = 0; i < triangleCount; i++)
			{
				//Debug.Log(newTriangles[i] + " -> " + (newTriangles[i] - shift[newTriangles[i]]));
				newTriangles[i] -= shift[newTriangles[i]];
			}
			mesh.Clear();
			mesh.vertices = newVertices;
			if(useUV)
			{
				mesh.uv = newUV;
			}
			if(useUV1)
			{
				mesh.uv1 = newUV1;
			}
			if(useUV2)
			{
				mesh.uv2 = newUV2;
			}
			if(useTangents)
			{
				mesh.tangents = newTangents;
			}
			if(useNormals)
			{
				mesh.normals = newNormals;
			}
			mesh.triangles = newTriangles;
		}
		#endregion
		
		#region SHEARING
		
		public static Mesh GetSheared(this Mesh mesh, Vector2 xShear, Vector2 yShear, Vector2 zShear)
		{
			mesh = mesh.GetShearedXAboutY(xShear.x);
			mesh.ShearMeshXAboutZ(xShear.y);
			mesh.ShearMeshYAboutX(yShear.x);
			mesh.ShearMeshYAboutZ(yShear.y);
			mesh.ShearMeshZAboutX(zShear.x);
			mesh.ShearMeshZAboutY(zShear.y);
			return mesh;
		}
		
		public static Mesh GetShearedXAboutY(this Mesh mesh, float amount)
		{
			return mesh.GetMorphed(Vector3.up, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static Mesh GetShearedXAboutZ(this Mesh mesh, float amount)
		{
			return mesh.GetMorphed( Vector3.forward, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static Mesh GetShearedYAboutX(this Mesh mesh, float amount)
		{
			return mesh.GetMorphed(Vector3.right, new Vector3(0, 1, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static Mesh GetShearedYAboutZ(this Mesh mesh, float amount)
		{
			return mesh.GetMorphed( Vector3.forward, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		public static Mesh GetShearedZAboutY(this Mesh mesh, float amount)
		{
			return mesh.GetMorphed(Vector3.up, new Vector3(0, 0, amount), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		
		public static void Shear(this Mesh mesh, Vector2 xShear, Vector2 yShear, Vector2 zShear)
		{
			mesh.ShearMeshXAboutY(xShear.x);
			mesh.ShearMeshXAboutZ(xShear.y);
			mesh.ShearMeshYAboutX(yShear.x);
			mesh.ShearMeshYAboutZ(yShear.y);
			mesh.ShearMeshZAboutX(zShear.x);
			mesh.ShearMeshZAboutY(zShear.y);
		}
		
		public static void ShearMeshXAboutY(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.up, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static void ShearMeshXAboutZ(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.forward, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static void ShearMeshYAboutX(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.right, new Vector3(0, 1, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static void ShearMeshYAboutZ(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.forward, new Vector3(amount, 0, 0), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		
		public static void ShearMeshZAboutX(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.right, new Vector3(0, 0, amount), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		
		public static void ShearMeshZAboutY(this Mesh mesh, float amount)
		{
			mesh.Morph(Vector3.up, new Vector3(0, 0, amount), Quaternion.identity, Vector3.zero, Geom.DEFAULT_RESOLUTION);
		}
		#endregion
		
		#region MORPHING
		public static Mesh GetMorphed(this Mesh mesh, Vector3 dir, Vector3 pos, Quaternion q, Vector3 s, int resolution)
		{
			Mesh morphed = new Mesh();
			morphed.vertices = mesh.vertices;
			morphed.triangles = mesh.triangles;
			morphed.uv = mesh.uv;
			morphed.uv1 = mesh.uv1;
			morphed.uv2 = mesh.uv2;
			morphed.tangents = mesh.tangents;
			morphed.Morph(dir, pos, q, s, resolution);
			return morphed;
		}
		
		public static void Morph(this Mesh mesh, Vector3 dir, Vector3 pos, Quaternion q, Vector3 s, int resolution)
		{
			if(mesh == null)
			{
				return;
			}
			if(resolution < 1)
			{
				resolution = 1;
			}
			if(q.x == 0 && q.y == 0 && q.z == 0 && q.w == 0)
			{
				q = Quaternion.identity;
			}
			dir.Normalize();
			Vector3[] vertices = mesh.vertices;
			int numVertices = vertices.Length;
			Matrix4x4[] matrixes = new Matrix4x4[resolution];
			float step = 1F / resolution;
			
			//generating the transform matrixes
			for(int i = 0; i < resolution; i++)
			{
				matrixes[i] = Matrix4x4.TRS((i*step*pos)-pos, Quaternion.Slerp(Quaternion.Inverse(q), q, i*step), Vector3.one + (s*i*step*2) - s);
			}
			
			//getting the vertice positions along the dir normal
			float maxPos = float.MinValue, minPos = float.MaxValue;
			float[] positions = new float[numVertices];
			for(int i = 0; i < numVertices; i++)
			{
				float vPos = Vector3.Dot(vertices[i], dir);
				if(vPos > maxPos)
					maxPos = vPos;
				else if(vPos < minPos)
					minPos = vPos;
				positions[i] = vPos;
			}
			float unitPos = resolution / (maxPos + 0.01F - minPos);
			// the actual morphing
			for(int i = 0; i < numVertices; i++)
			{
				int matrixIndex = (int)((positions[i] - minPos) * unitPos);
				vertices[i] = (matrixes[matrixIndex]).MultiplyPoint3x4(vertices[i]);
			}
			// clean up
			mesh.vertices = vertices;
			mesh.RecalculateNormals();
		}
		#endregion
		
		#region CLIPPING
		
		public class VertexNode
		{
			public static int Counter;
			public static LinkedList<VertexNode> AllSplits;
			
			public readonly Vector3 Position;
			public readonly Vector3 Normal;
			public readonly Vector4 Tangent;
			public readonly Vector2 UV;
			public readonly Vector2 UV1;
			public readonly Vector2 UV2;
			public readonly int Side;
			public Dictionary<VertexNode, VertexNode> Splits;
			public int Index = -1;
			
			public static void Init()
			{
				Counter = 0;
				AllSplits = new LinkedList<VertexNode>();
			}
			
			public static void AddSplit(VertexNode a, VertexNode b, VertexNode split)
			{
				if(a.Splits == null) a.Splits = new Dictionary<VertexNode, VertexNode>();
				if(b.Splits == null) b.Splits = new Dictionary<VertexNode, VertexNode>();
				a.Splits.Add(b, split);
				b.Splits.Add(a, split);
				AllSplits.AddLast(split);
			}
			public VertexNode GetSplit(VertexNode target, Plane plane)
			{
				if(Side == target.Side) return target;
				if(Splits != null)
				{
					if(Splits.ContainsKey(target))
						return Splits[target];
				}
				if(Side != 0 && target.Side != 0)
				{
					float slerp = Mathf.Abs(plane.GetDistanceToPoint(Position));
					slerp = slerp / (Mathf.Abs(plane.GetDistanceToPoint(target.Position)) + slerp);
					VertexNode split = new VertexNode(this, target, slerp);
					AddSplit(this, target, split);
					return split;
				}
				return target;
			}
			public VertexNode(VertexNode a, VertexNode b, float slerp)
			{
				float invSlerp = 1F - slerp;
				Position = a.Position * invSlerp + b.Position * slerp;
				
				if(a.Normal.Dot(b.Normal).Abs() > 0.01F) Normal = (a.Normal * invSlerp + b.Normal * slerp).normalized;
				else Normal = b.Normal;
				if(a.Tangent.XYZ().Dot(b.Tangent.XYZ()).Abs() > 0.01F) Tangent = (a.Tangent * invSlerp + b.Tangent * slerp).normalized;
				else Tangent = b.Tangent;
				
				UV = a.UV * invSlerp + b.UV * slerp;
				UV1 = a.UV1 * invSlerp + b.UV1 * slerp;
				UV2 = a.UV2 * invSlerp + b.UV2 * slerp;
				Side = 0;
				Counter++;
			}
			public VertexNode(Vector3 position, Vector3 normal, Vector4 tangent, Vector2 uv, Vector2 uv1, Vector2 uv2, Plane plane)
			{
				Position = position;
				Normal = normal;
				Tangent = tangent;
				UV = uv;
				UV1 = uv1;
				UV2 = uv2;
				Side = plane.GetSide(Position) ? 1 : -1;
				if(Side >= 0) Counter++;
			}
			public VertexNode(Vector3[] positions, Vector3[] normals, Vector4[] tangents, Vector2[] uvs, Vector2[] uvs1, Vector2[] uvs2, Plane plane, int index)
			{
				Position = (positions != null && positions.Length > index) ? positions[index] : Vector3.zero;
				Normal = (normals != null && normals.Length > index) ? normals[index] : Vector3.zero;
				Tangent = (tangents != null && tangents.Length > index) ? tangents[index] : Vector4.zero;
				UV = (uvs != null && uvs.Length > index) ? uvs[index] : Vector2.zero;
				UV1 = (uvs1 != null && uvs1.Length > index) ? uvs1[index] : Vector2.zero;
				UV2 = (uvs2 != null && uvs2.Length > index) ? uvs2[index] : Vector2.zero;
				Side = plane.GetSide(Position) ? 1 : -1;
				if(Side >= 0) Counter++;
			}
		}
		
		public static int[] GetClipped(this Mesh mesh, out Mesh clippedMesh, Plane plane, Func<VertexNode, bool> filter = null)
		{
			clippedMesh = mesh.Clone();
			return clippedMesh.Clip(plane, filter);
		}
		public static int[] Clip(this Mesh mesh, Plane plane, Func<VertexNode, bool> filter = null)
		{
			mesh.RemoveDuplicateVertices();
			VertexNode.Init();
			
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			Vector2[] uvs1 = mesh.uv1 ?? mesh.uv;
			Vector2[] uvs2 = mesh.uv2 ?? mesh.uv;
			int[] triangleIndices = mesh.triangles;
			
			int count = vertices.Length;
			LinkedList<VertexNode> triangles = new LinkedList<VertexNode>();
			LinkedList<VertexNode> clipEdges = new LinkedList<VertexNode>();
			VertexNode[] points = new VertexNode[count];
			
			for(int i = 0; i < count; i++)
			{
				points[i] = new VertexNode(vertices, normals, tangents, uvs, uvs1, uvs2, plane, i);
			}
			
			count = triangleIndices.Length;
			for(int i = 0; i < count; i+=3)//for each triangle
			{
				VertexNode a = points[triangleIndices[i+0]];
				VertexNode b = points[triangleIndices[i+1]];
				VertexNode c = points[triangleIndices[i+2]];
				
				if(a.Side < 0 && b.Side < 0 && c.Side < 0)//if all are on the side that's being removed
				{
					continue;
				}
				else if(filter != null && (filter(a) || filter(b) || filter(c)))//if anyone satisfies the filter for removal
				{
					continue;
				}
				else if(a.Side > 0 && b.Side > 0 && c.Side > 0)//if all are on the side that's NOT being removed
				{
					triangles.AddLast(a);
					triangles.AddLast(b);
					triangles.AddLast(c);
				}
				else//split occurs
				{
					VertexNode ab = a.GetSplit(b, plane);
					VertexNode bc = b.GetSplit(c, plane);
					VertexNode ca = c.GetSplit(a, plane);
					
					VertexNode edge1 = null;
					VertexNode edge2 = null;
					
					//there's always one edge that doesn't split, here I'm figuring out which it is
					if(ab == b)//is it ab
					{
						if(c.Side < 0)//if c is on the side being removed
						{
							triangles.AddLast(a);
							triangles.AddLast(b);
							triangles.AddLast(bc);
							
							triangles.AddLast(a);
							edge1 = bc;
							edge2 = ca;
						}
						else//a and b are on the side being removed
						{
							triangles.AddLast(c);
							edge1 = ca;
							edge2 = bc;
						}
					}
					else if(bc == c)//is it bc
					{
						if(a.Side < 0)//if a is on the side being removed
						{
							triangles.AddLast(b);
							triangles.AddLast(c);
							triangles.AddLast(ca);
							
							triangles.AddLast(b);
							edge1 = ca;
							edge2 = ab;
						}
						else//b and c are on the side being removed
						{
							triangles.AddLast(a);
							edge1 = ab;
							edge2 = ca;
						}
					}
					else//it's ca
					{
						if(b.Side < 0)//if b is on the side being removed
						{
							triangles.AddLast(c);
							triangles.AddLast(a);
							triangles.AddLast(ab);
							
							triangles.AddLast(c);
							edge1 = ab;
							edge2 = bc;
						}
						else//c and a are on the side being removed
						{
							triangles.AddLast(b);
							edge1 = bc;
							edge2 = ab;
						}
					}
					triangles.AddLast(edge1);
					triangles.AddLast(edge2);
					
					clipEdges.AddLast(edge2);
					clipEdges.AddLast(edge1);
				}
			}
			count = VertexNode.Counter;
			vertices = new Vector3[count];
			normals = new Vector3[count];
			tangents = new Vector4[count];
			uvs = new Vector2[count];
			uvs1 = new Vector2[count];
			uvs2 = new Vector2[count];
			triangleIndices = new int[triangles.Count];
			
			
			//adding prevous existing points that were not removed
			count = points.Length;
			int currentIndex = 0;
			for(int i = 0; i < count; i++)
			{
				if(points[i].Side >= 0)
				{
					vertices[currentIndex] = points[i].Position;
					normals[currentIndex] = points[i].Normal;
					tangents[currentIndex] = points[i].Tangent;
					uvs[currentIndex] = points[i].UV;
					uvs1[currentIndex] = points[i].UV1;
					uvs2[currentIndex] = points[i].UV2;
					
					points[i].Index = currentIndex++;
				}
			}
			//adding new points (splits)
			foreach(VertexNode node in VertexNode.AllSplits)
			{
				vertices[currentIndex] = node.Position;
				normals[currentIndex] = node.Normal;
				tangents[currentIndex] = node.Tangent;
				uvs[currentIndex] = node.UV;
				uvs1[currentIndex] = node.UV1;
				uvs2[currentIndex] = node.UV2;
				
				node.Index = currentIndex++;
			}
			//adding triangles based on each VertexNode.Index
			currentIndex = 0;
			foreach(VertexNode node in triangles)
			{
				if(node.Side < 0) throw new ArgumentOutOfRangeException("trying to add vertex at index: " + node.Index + ". No can do, for it is on the wrong side of the clipping plane");
				triangleIndices[currentIndex++] = node.Index;
			}
			
			currentIndex = 0;
			int[] edges = new int[clipEdges.Count];
			foreach(VertexNode node in clipEdges)
			{
				edges[currentIndex++] = node.Index;
			}
			
			mesh.Clear();
			
			mesh.vertices = vertices;
			mesh.normals = normals;
			mesh.tangents = tangents;
			mesh.uv = uvs;
			if(mesh.uv1 != null && mesh.uv1.Length > 0) mesh.uv1 = uvs1;
			if(mesh.uv2 != null && mesh.uv2.Length > 0) mesh.uv2 = uvs2;
			mesh.triangles = triangleIndices;
			
			return edges;
		}
		
		#region OLD STUFF
		
		public static VertexList Split(this Mesh mesh, Plane plane)
		{
			bool allDestroyed = true;
			
			Vector3[] vertices = mesh.vertices;
			Vector3[] normals = mesh.normals;
			Vector4[] tangents = mesh.tangents;
			Vector2[] uvs = mesh.uv;
			int[] triangles = mesh.triangles;
			
			int numTriangles = triangles.Length;
			
			List<int> newTriangles = new List<int>();
			List<Vector3> newVertices = new List<Vector3>();
			List<Vector3> newNormals = new List<Vector3>();
			List<Vector4> newTangents = new List<Vector4>();
			List<Vector2> newUVs = new List<Vector2>();
			List<Vector3> cutVertices = new List<Vector3>();
			List<Vector2> cutUVs = new List<Vector2>();
			List<int> cutIndices = new List<int>();
			
			for(int i = 0; i < numTriangles; i += 3)
			{
				
				int p1Index = triangles[i];
				int p2Index = triangles[i + 1];
				int p3Index = triangles[i + 2];
				
				bool p1 = plane.GetSide(vertices[p1Index]);
				bool p2 = plane.GetSide(vertices[p2Index]);
				bool p3 = plane.GetSide(vertices[p3Index]);
				
				allDestroyed = allDestroyed && p1 && p2 && p3;
				
				if (!(p1 || p2 || p3)) {
					newVertices.AddRange(new Vector3[] { vertices[p1Index], vertices[p2Index], vertices[p3Index] });
					newNormals.AddRange(new Vector3[] { normals[p1Index], normals[p2Index], normals[p3Index] });
					newTangents.AddRange(new Vector4[] { tangents[p1Index], tangents[p2Index], tangents[p3Index] });
					newUVs.AddRange(new Vector2[] { uvs[p1Index], uvs[p2Index], uvs[p3Index] });
					newTriangles.AddRange(new int[] { newVertices.Count - 3, newVertices.Count - 2, newVertices.Count - 1 });
					//newTriangles.AddRange (triangles.GetRange (i, 3));
				}
				else if (!p1) {
					if (!p2) {
						//!p1 && !p2 && p3
						SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices, newVertices, newNormals, newTangents, newUVs, newTriangles, p3Index, p1Index,
						p2Index,
						true);
					}
					else if (!p3) {
						//!p1 && p2 && !p3
						SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices,  newVertices, newNormals, newTangents, newUVs, newTriangles, p2Index, p3Index,
						p1Index,
						true);
					}
					else {
						//!p1 && p2 && p3
						SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices,  newVertices, newNormals, newTangents, newUVs, newTriangles, p1Index, p3Index,
						p2Index,
						false);
					}
				}
				else if (!p2) {
					if (!p3) {
						//p1 && !p2 && !p3
						SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices,  newVertices, newNormals, newTangents, newUVs, newTriangles, p1Index, p2Index,
						p3Index,
						true);
					}
					else {
						//p1 && !p2 && p3
						SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices,  newVertices, newNormals, newTangents, newUVs, newTriangles, p2Index, p1Index,
						p3Index,
						false);
					}
				}
				else if (!p3) {
					//p1 && p2 && !p3
					SplitTriangle(plane, vertices, normals, tangents, uvs, triangles, cutVertices, cutUVs, cutIndices,  newVertices, newNormals, newTangents, newUVs, newTriangles, p3Index, p2Index,
					p1Index,
					false);
				}
			}
			mesh.Clear();
			mesh.vertices = newVertices.ToArray ();
			mesh.normals = newNormals.ToArray ();
			mesh.tangents = newTangents.ToArray ();
			mesh.uv = newUVs.ToArray ();
			mesh.triangles = newTriangles.ToArray ();
			return allDestroyed ? null : new VertexList(cutVertices.ToArray (), cutUVs.ToArray(), cutIndices.ToArray());
		}
		#endregion
		
		#endregion
		
		#region private
		
		private const int _CONECTION_LIMITS = 33;
		
		private static SharpEdge[] _edges;
		private static int[][] _connections;
		private static float[] _normals;
		
		private static float SignedVolumeOfTriangle(Vector3 p1, Vector3 p2, Vector3 p3)
		{
		    float v321 = p3.x * p2.y * p1.z;
		    float v231 = p2.x * p3.y * p1.z;
		    float v312 = p3.x * p1.y * p2.z;
		    float v132 = p1.x * p3.y * p2.z;
		    float v213 = p2.x * p1.y * p3.z;
		    float v123 = p1.x * p2.y * p3.z;
		    return (1.0F / 6.0F) * (-v321 + v231 + v312 - v132 - v213 + v123);
		}
		
		private static void SplitTriangle(Plane plane, Vector3[] oldVertices, Vector3[] oldNormals, Vector4[] oldTangents, Vector2[] oldUVs, int[] oldTriangles, List<Vector3> cutVertices, List<Vector2> cutUVs, List<int> cutIndices, List<Vector3> newVertices, List<Vector3> newNormals, List<Vector4> newTangents, List<Vector2> newUVs, List<int> newTriangles, int backIndex, int front1Index,
		int front2Index, bool useFront)
		{
			Vector3 back = oldVertices[backIndex];
			Vector3 front1 = oldVertices[front1Index];
			Vector3 front2 = oldVertices[front2Index];
			
			Vector3 backN = oldNormals[backIndex];
			Vector3 front1N = oldNormals[front1Index];
			Vector3 front2N = oldNormals[front2Index];
			
			Vector4 backT = oldTangents[backIndex];
			Vector4 front1T = oldTangents[front1Index];
			Vector4 front2T = oldTangents[front2Index];
			
			float distToBack = plane.GetDistanceToPoint(back);
			float distToFront1 = plane.GetDistanceToPoint(front1);
			float distToFront2 = plane.GetDistanceToPoint(front2);
			if (distToBack < 0) {
				distToBack = -distToBack;
			}
			if (distToFront1 < 0) {
				distToFront1 = -distToFront1;
				distToFront2 = -distToFront2;
			}
			float dist1 = distToBack + distToFront1;
			float dist2 = distToBack + distToFront2;
			
			//Vector3[] splitProduct = splitTriangle (plane, vertices[triangles[i + 2]], vertices[triangles[i]], vertices[triangles[i + 1]]);
					/*vertices[triangles[i + 2]] = back * distToFront1 / dist1 + front1 * distToBack / dist1;
			newVertices.Add (back * distToFront2 / dist2 + front2 * distToBack / dist2);
			newTriangles.AddRange (triangles.GetRange (i, 3));
			newTriangles.AddRange (new Vector3 { triangles[i + 2], triangles[i + 1], numVertices + newVertices.Count - 1 });
			*/
		
			float ratio1 = distToBack / dist1;
			float ratio2 = distToBack / dist2;
			
			if (useFront) {
				newVertices.AddRange (new Vector3[] { front1, front2, Vector3.Lerp(back, front1, ratio1), Vector3.Lerp(back, front2, ratio2) });
				newNormals.AddRange (new Vector3[] { front1N, front2N, Vector3.Lerp(backN, front1N, ratio1), Vector3.Lerp(backN, front2N, ratio2) });
				newTangents.AddRange (new Vector4[] { front1T, front2T, Vector4.Lerp(backT, front1T, ratio1), Vector4.Lerp(backT, front2T, ratio2) });
				newUVs.AddRange (new Vector2[] { oldUVs[front1Index], oldUVs[front2Index], Vector2.Lerp(oldUVs[backIndex], oldUVs[front1Index], ratio1), Vector2.Lerp(oldUVs[backIndex], oldUVs[front2Index], ratio2) });
				newTriangles.AddRange (new int[] { newVertices.Count - 4, newVertices.Count - 3, newVertices.Count - 2, newVertices.Count - 1, newVertices.Count - 2, newVertices.Count - 3 });
			}
			else {
				newVertices.AddRange (new Vector3[] { back, Vector3.Lerp(back, front1, ratio1), Vector3.Lerp(back, front2, ratio2) });
				newNormals.AddRange (new Vector3[] { backN, Vector3.Lerp(backN, front1N, ratio1), Vector3.Lerp(backN, front2N, ratio2) });
				newTangents.AddRange (new Vector4[] { backT, Vector4.Lerp(backT, front1T, ratio1), Vector4.Lerp(backT, front2T, ratio2) });
				newUVs.AddRange (new Vector2[] { oldUVs[backIndex], Vector2.Lerp(oldUVs[backIndex], oldUVs[front1Index], ratio1), Vector2.Lerp(oldUVs[backIndex], oldUVs[front2Index], ratio2)});
				newTriangles.AddRange (new int[] { newVertices.Count - 3, newVertices.Count - 1, newVertices.Count - 2 });
			}
			int indexA = newVertices.Count - 2;
			int indexB = newVertices.Count - 1;
			cutVertices.AddRange (new Vector3[] { newVertices[indexA], newVertices[indexB] });
			cutUVs.AddRange	(new Vector2[] { newUVs[indexA], newUVs[indexB] });
			cutIndices.AddRange (new int[] { indexA, indexB });
		}
		private static Vector3[] SplitTriangle (Plane plane, Vector3 back, Vector3 front1, Vector3 front2)
		{
			float distToBack =  plane.GetDistanceToPoint(back);
			float distToFront1 =  plane.GetDistanceToPoint(front1);
			float distToFront2 =  plane.GetDistanceToPoint(front2);
			float dist1 = distToBack + distToFront1;
			float dist2 = distToBack + distToFront2;
			
			return new Vector3[]{ back * distToFront1 / dist1 + front1 * distToBack / dist1, back * distToFront2 / dist2 + front2 * distToBack / dist2};
		}
		
		#endregion
	}
}

